package it.unipg.bipod.applicationLogic;

import it.unipg.bipod.dataAccess.*;
import it.unipg.bipod.dataModel.*;

import java.sql.Timestamp;
import java.io.IOException;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.Vector;

/**
 * DocenteApplication gestisce le operazioni che un docente puo' compiere su BiPoD.<br>
 * Ogni istanza di DocenteApplication è associata a una sessione di accesso di un docente
 * al sistema.<br>
 * Tutti i metodi di questa classe lanciano un'eccezione di tipo {@link ApplicationException}
 * quando l'operazione non può essere compiuta a causa di un errore. Gli errori dovuti
 * a motivi "fisiologici" (come ad esempio record già presenti nelle basi di dati o simili)
 * sono sottoclassi di {@link ApplicationException}, mentre gli altri sono istanze di
 * {@link ApplicationException} contenenti una breve descrizione sulla natura dell'errore.
 * In generale la descrizione delle eccezioni non contiene dati sensibili, percui puo'
 * essere anche direttamente visualizata all'utente.<br>
 * Tutti i metodi si aspettano che gli argomenti siano già ben formati, quindi non
 * effettuano nessun controllo in questo senso.
 * 
 * @author Lorenzo Porzi
 * @see Application
 * @see AuthManager
 */
public class DocenteApplication extends Application {
	private Docente docente; // L'entità Docente associata alla sessione corrente
	private Registrazione registrazione; // L'entità Registrazione associata alla sessione corrente
	
	/**
	 * Costruisce una DocenteApplication associata ad un docente e la sua registrazione.
	 * 
	 * @param dataManager il data manager che questo oggetto dovra' usare.
	 * @param docente il docente associato a questo oggetto.
	 * @param registrazione la registrazione del docente associato a questo oggetto.
	 * @throws IOException
	 */
	DocenteApplication(DataManager dataManager, Docente docente, Registrazione registrazione) throws IOException {
		super(dataManager);
		this.docente = docente;
		this.registrazione = registrazione;
	}
	
	/* ----------
	 * Operazioni
	 * ---------- */
	
	/**
	 * Inoltra una nuova richiesta di assegnamento per l'insegnamento specificato.
	 * 
	 * @param insegnamento l'insegnamento per il quale fare richiesta.
	 * @throws BandoScadutoException se l'insegnamento appartiene ad un bando non attivo.
	 * @throws RichiestaEffettuataException se il docente ha gia' effettuato una richiesta
	 * per questo insegnamento.
	 * @throws ApplicationException
	 */
	public void nuovaRichiesta(Insegnamento insegnamento) throws ApplicationException {
		try {
			DataManager dataManager = getDataManager();
			Timestamp now = new Timestamp(System.currentTimeMillis());
			
			// Verifico che il bando a cui appartiene l'insegnamento sia attivo
			Bando bando = dataManager.getBandoFromId(insegnamento.getBando());
			if (bando.getDataScadenza().before(now) || bando.getDataPubblicazione().after(now))
				throw new BandoScadutoException();
			
			// Verifico di non aver già effettuato una richiesta allo stesso insegnamento
			Richiesta[] richieste = dataManager.getRichiestaFromInsegnamento(insegnamento.getIdInsegnamento());
			for (Richiesta richiesta : richieste)
				if (richiesta.getDocente() == registrazione.getIdRegistrazione())
					throw new RichiestaEffettuataException();
			
			// Creo e inserisco la richiesta
			Richiesta richiesta = new Richiesta(0,
					now,
					docente.getQualifica(),
					docente.getSsd(),
					registrazione.getIdRegistrazione(),
					insegnamento.getIdInsegnamento());
			dataManager.insertEntity(richiesta);
			
			// Registro l'operazione
			Operazione operazione = Logger.log(dataManager, Operazione.NUOVA_RICHIESTA, registrazione, docente.getNomeUtente() +
					" inoltra una richiesta di assegnamento");
			
			// Invio al docente l'email di conferma
			String testoEmail = getResManager().getResource("confermaRichiestaMail");
			testoEmail = testoEmail.replaceAll("<insegnamento>", insegnamento.getNome());
			testoEmail = testoEmail.replaceAll("<bando>", SimpleDateFormat.getInstance().format(bando.getDataPubblicazione()));
			testoEmail = testoEmail.replaceAll("<hash>", operazione.getHash());
			testoEmail = testoEmail.replaceAll("<operazione>", operazione.toString());
			String soggetto = getSettings().getProperty("confermaRichiestaSubject");
			Mailer.sendMail(testoEmail, soggetto, docente.getEmail());
			
		} catch (DataAccessException e) {
			throw new ApplicationException("Errore di accesso al database");
		} catch (NoSuchAlgorithmException e) {
			throw new ApplicationException("Errore");
		}
	}
	
	/**
	 * Cancella la richiesta di assegnamento che si riferisce all'insegnamento specificato.<br>
	 * L'operazione riesce solamente se la richiesta si riferisce a un insegnamento appartenente
	 * ad un bando attivo.
	 * 
	 * @param insegnamento l'insegnamento da cui cancellare la richiesta.
	 * @throws BandoScadutoException se l'insegnamento appartiene ad un bando non attivo.
	 * @throws ApplicationException
	 */
	public void cancellaRichiesta(Insegnamento insegnamento) throws ApplicationException {
		try {
			DataManager dataManager = getDataManager();
			Timestamp now = new Timestamp(System.currentTimeMillis());
			
			// Recupero la richiesta corrispondente all'insegnamento
			Richiesta[] richieste = dataManager.getRichiestaFromInsegnamento(insegnamento.getIdInsegnamento());
			Richiesta richiesta = null;
			for (Richiesta richiestaTrovata : richieste)
				if (richiestaTrovata.getDocente() == registrazione.getIdRegistrazione())
					richiesta = richiestaTrovata;
			if (richiesta == null)
				throw new ApplicationException("Richiesta non trovata");
			
			// Verifico che il bando a cui appartiene l'insegnamento sia attivo
			Bando bando = dataManager.getBandoFromId(insegnamento.getBando());
			if (bando.getDataScadenza().before(now) || bando.getDataPubblicazione().after(now))
				throw new BandoScadutoException();
			
			// Cancello la richiesta
			dataManager.removeEntity(richiesta);
			
			// Registro l'operazione
			Logger.log(dataManager, Operazione.CANCELLA_RICHIESTA, registrazione, docente.getNomeUtente() + " cancella una richiesta di assegnamento");
			
		} catch (DataAccessException e) {
			throw new ApplicationException("Errore di accesso al database");
		} catch (NoSuchAlgorithmException e) {
			throw new ApplicationException("Errore");
		}
	}
	
	/**
	 * Modifica i dati personali del docente.
	 * 
	 * @param email la nuova email
	 * @param nome il nuovo nome
	 * @param cognome il nuovo cognome
	 * @param ssd il nuovo settore scientifico-disciplinare
	 * @param qualifica la nuova qualifica
	 * @throws ApplicationException
	 */
	public void modificaDati(String email, String nome, String cognome, String ssd, String qualifica) throws ApplicationException {
		try {
			DataManager dataManager = getDataManager();
			
			// Aggiorna i dati
			docente.setEmail(email);
			docente.setNome(nome);
			docente.setCognome(cognome);
			docente.setSsd(ssd);
			docente.setQualifica(qualifica);
			dataManager.updateEntity(docente);
			
			// Registro l'operazione
			Logger.log(dataManager, Operazione.MODIFICA_REGISTRAZIONE, registrazione, docente.getNomeUtente() + " modifica i suoi dati personali");
			
		} catch (DataAccessException e) {
			throw new ApplicationException("Errore di accesso al database");
		} catch (NoSuchAlgorithmException e) {
			throw new ApplicationException("Errore");
		}
	}
	
	/* -------------------------
	 * Metodi di accesso ai dati
	 * ------------------------- */
	
	/**
	 * Recupera tutte le richieste effettuate da questo docente.
	 * 
	 * @param docente il docente
	 * @return un array di {@link Richiesta} con i dati estratti
	 * @throws ApplicationException
	 */
	public Richiesta[] getRichiesteEffettuate() throws ApplicationException {
		try {
			DataManager dataManager = getDataManager();
			
			Registrazione registrazione = dataManager.getRegistrazioneFromUtente(docente.getNomeUtente());
			Richiesta[] richieste = dataManager.getRichiestaFromRegistrazione(registrazione.getIdRegistrazione());
			
			return richieste;
		} catch (DataAccessException e) {
			throw new ApplicationException("Errore di accesso al database");
		}
	}
	
	/**
	 * Recupera tutti i bandi attivi.
	 * 
	 * @return un array di {@link Bando} con i bandi estratti oppure {@code null}
	 * @throws ApplicationException
	 */
	public Bando[] getBandi() throws ApplicationException {
		try {
			DataManager dataManager = getDataManager();
			
			// Recupera i bandi
			Object[] bandi = dataManager.getAll(Bando.class);
			
			// Seleziona i bandi attivi
			Vector<Bando> attivi = new Vector<Bando>();
			java.sql.Date now = new java.sql.Date(System.currentTimeMillis());
			for (Object b : bandi) {
				Bando bando = (Bando)b;
				
				if (bando.getDataScadenza().after(now) && bando.getDataPubblicazione().before(now))
					attivi.add(bando);
			}
			
			// Ritorna null se non ci sono bandi attivi
			if (attivi.size() == 0)
				return null;
				
			Bando[] bandiAttivi = new Bando[attivi.size()];
			attivi.toArray(bandiAttivi);
			
			return bandiAttivi;
		} catch (DataAccessException e) {
			throw new ApplicationException("Errore di accesso al database");
		}
	}
	
	/**
	 * Recupera tutti gli insegnamenti appartenenti ad un certo bando.
	 * 
	 * @param bando il bando
	 * @return un array di {@link InsegnamentoCorso} con i dati recuperati.
	 * @throws ApplicationException
	 */
	public InsegnamentoCorso[] getInsegnamenti(Bando bando) throws ApplicationException {
		try {
			DataManager dataManager = getDataManager();
			
			// Recupera gli insegnamenti
			Insegnamento[] insegnamenti = dataManager.getInsegnamentoFromBando(bando.getIdBando());
			
			// Crea il risultato
			InsegnamentoCorso[] insegnamentiCorso = new InsegnamentoCorso[insegnamenti.length];
			for (int i = 0; i < insegnamenti.length; ++i) {
				Corso corso = dataManager.getCorsoFromId(insegnamenti[i].getCorso());
				
				insegnamentiCorso[i] = new InsegnamentoCorso(insegnamenti[i], corso, bando.getDataPubblicazione());
			}
			
			return insegnamentiCorso;
		} catch (DataAccessException e) {
			throw new ApplicationException("Errore di accesso al database");
		}
	}
	
	/**
	 * Recupera l'insegnamento a cui si riferisce la richiesta specificata
	 * 
	 * @param richiesta l'oggetto {@link Richiesta} di cui recuperare l'insegnamento
	 * @return un oggetto {@link InsegnamentoCorso} con i dati recuperati
	 * @throws ApplicationException
	 */
	public InsegnamentoCorso getInsegnamento(Richiesta richiesta) throws ApplicationException {
		try {
			DataManager dataManager = getDataManager();
			
			// Recupera gli insegnamenti
			Insegnamento insegnamento = dataManager.getInsegnamentoFromId(richiesta.getInsegnamento());
			Corso corso = dataManager.getCorsoFromId(insegnamento.getCorso());
			Bando bando = dataManager.getBandoFromId(insegnamento.getBando());
			
			// Crea il risultato
			InsegnamentoCorso insegnamentoCorso = new InsegnamentoCorso(insegnamento, corso, bando.getDataPubblicazione());
			
			return insegnamentoCorso;
		} catch (DataAccessException e) {
			throw new ApplicationException("Errore di accesso al database");
		}
	}
	
	/**
	 * Restituisce il docente associato a questo oggetto.
	 * 
	 * @return il docente di questa DocenteApplication.
	 */
	public Docente getDocente() {
		return docente;
	}
	
	/**
	 * Restituisce la registrazione associata a questo oggetto.
	 * 
	 * @return la registrazione di questa DocenteApplication.
	 */
	public Registrazione getRegistrazione() {
		return registrazione;
	}
}